ホームに戻る
出典 :
[C# 入門] タプル(tuple)の使い方まとめ 【C#入門】C#のタプル(tuple)を使ってできることとは | .NETコラム ValueTupleをTuple・匿名型と比較してみる - Qiita タプル - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
関連 :
[C#]レコード [C++]タプル
目次 :

タプル(tuple)とは

複数の値を組にしたデータ構造のこと。
過去にはTuple型、匿名型を用いることができたが、C#7.0より ValueTuple 型が追加された。
以下、特記なき限りは ValueTuple 型について論じるものとする。

構造体(struct)との違い

複数の値を組にできる点は構造体と同様であるが、タプル( ValueTuple )の場合は要素(フィールド)名を必要としないなど、構造体よりも簡便に用いることができる。
(逆に、Tuple 型では要素名を割り当てることができない。)
構造体と異なり事前に型定義を必要としないため、特にメソッドの戻り値として用いる場合に有用である。

構造体

メソッドの戻り値として用いる場合、事前に型定義が必要となる。
// 構造体型 Hoge 定義 struct Hoge { public int no; public string name; } : // Hoge 型を返すメソッド Func() public Hoge Func() { Hoge retval; retval.no = 20; retval.name "ttt"; return retval; }

タプル(ValueTuple)

メソッドの戻り値に用いる場合も、事前の型定義が不要で、簡便に記述できる。
// タプルを返すメソッド Func() // 要素名( no 、name )は省略可能 public (int no, string name) Func() { return (20, "ttt"); }

使用例

タプルの宣言と代入

宣言、代入のいずれのタイミングでもフィールド名を指定(変更)できる。また、すべてのフィールドに名前をつける必要はない(有名・無名フィールドが混在してもよい)。
// 名前なしタプル (int, string, bool, double) unnamed_tuple = (100, "abcde", true, 1.25); // 宣言時に名前を指定 (int value1, string value2, bool value3, double value4) named_tuple1 = (100, "abcde", true, 1.25); // 代入時に名前を指定 (int, string, bool, double) named_tuple2 = (value1: 100, value2: "abcde", value3: true, value4: 1.25); // 型推論を使用可能 var named_tuple3 = (value1: 100, value2: "abcde", value3: true, value4: 1.25);

タプルの参照

設定された要素(フィールド)名、または先頭要素から順に Item1 、Item2 、… でアクセスが可能。0 起算ではない点に注意。
フィールド名を設定している場合でも Item1 、Item2 、… を用いることができる。
// 名前なしタプルの参照(Item#) System.Console.WriteLine(unnamed_tuple.Item1); //< 要素1 ⇒ 100 System.Console.WriteLine(unnamed_tuple.Item2); //< 要素2 ⇒ "abcde" System.Console.WriteLine(unnamed_tuple.Item3); //< 要素3 ⇒ true System.Console.WriteLine(unnamed_tuple.Item4); //< 要素4 ⇒ 1.25 // 名前つきタプルの参照 System.Console.WriteLine(named_tuple1.value1); //< 要素1 ⇒ 100 System.Console.WriteLine(named_tuple1.value2); //< 要素2 ⇒ "abcde" System.Console.WriteLine(named_tuple1.value3); //< 要素3 ⇒ true System.Console.WriteLine(named_tuple1.value4); //< 要素4 ⇒ 1.25

タプルの分解(要素の抽出)

ここで、unnamed_tuple は ValueTuple 型の変数とする。代入により、タプルの各要素を個別の変数に転写できる。
// 1 : 個別に型推論 (var value1, var value2, var value3, var value4) = unnamed_tuple; // 2 : まとめて型推論 // var ()内に変数を宣言 var (value1, value2, value3, value4) = unnamed_tuple; // 3 : 要素1、要素3のみを取得(他を読み飛ばす) var (valueM, _, valueN, _) = unnamed_tuple; // 4 : 既存の変数をタプル化 int field1; string field2; bool field3; double field4; (field1, field2, field3, field4) = unnamed_tuple;
上記の例1および2では、変数 value1 に unnamed_tuple.Item1 の値がコピーされる。value1 の型は初期値である unnamed_tuple.Item1 より推論される。
value2 から value4 も同様。
例3のように、抽出する必要のない要素は _ (アンダースコア)を指定することで読み飛ばすことができる。

タプルの比較

すべてのフィールドの値が等しい場合、ふたつのタプルは等しいとみなされる。
フィールド名は比較に関与しない(フィールド名が異なっていても問題とはならない)。
但し、両者の要素数が異なる場合は比較できず、例外が発生する。
var t1 = ( 100, "abcde", true, 1.25); //< 名前なし var t2 = (value1: 100, value2: "abcde", value3: true, value4: 1.25); //< 名前つき System.Console.WriteLine(t1 == t2); //< true var t3 = (100, "abcde", true, 1.25); var t4 = (100, "abcde", true); System.Console.WriteLine(t3 == t4); //< 要素数が異なるため例外発生

タプルを返すメソッド

class Program { // 名前なしタプルを返すメソッド public static (int, int) TestMethod_unnamed() { return (10, 20); } // 名前つきタプルを返すメソッド public static (int value1, int value2) TestMethod_named() { return (100, 200); } public static void Main() { // 1 : 名前なしタプルの受け取り var a = TestMethod_unnamed(); System.Console.WriteLine( a.Item1 ); System.Console.WriteLine( a.Item2 ); // 2 : 名前なしタプルを名前つきタプルで受け取り (var val1, var val2) b = TestMethod_unnamed(); System.Console.WriteLine( b.val1 ); System.Console.WriteLine( b.val2 ); // 3 : 名前つきタプルの受け取り var c = TestMethod_named(); System.Console.WriteLine( c.value1 ); System.Console.WriteLine( c.value2 ); // 4 : 名前つきタプルを別名で受け取り (var altVal1, var altVal2) d = TestMethod_named(); System.Console.WriteLine( d.altVal1 ); System.Console.WriteLine( d.altVal2 ); } }
上記のコードはいずれも有効である。
例4のように、メソッドで返されるタプルのフィールド名を新たに割り当てなおすことも可能だが、フィールド名は最後に割り当てたもののみが有効となる。

ValueTuple 、Tuple 、匿名型の比較

ValueTuple Tuple 匿名型
メンバの名前付け 不可
引数・戻り値としての使用 不可
型の種類 構造体 クラス クラス
メンバの種類 フィールド プロパティ プロパティ
可変・不変 ミュータブル イミュータブル イミュータブル
null格納 不可 不可
// ValueTupleの宣言・初期化 // 明示的に名前を付与しない限り、メンバに名前はつかない (int, string ) vt1 = ( 1, "vt"); //< 名前なし var vt2 = ( 1, "vt"); //< 名前なし ValueTuple<int, string > vt3 = ( 1, "vt"); //< 名前なし (int x, string y) vt4 = ( 1, "vt"); //< 名前つき(宣言時に名前付与) var vt5 = (x: 1, y: "vt"); //< 名前つき(初期化時に名前付与) // 匿名型の宣言・初期化 // クラス型のため new を用いる var at1 = new { x = 1, y = "a" }; //< 初期化時に名前付与 var m = 1; var n = "yy"; var at2 = new { m, n }; //< 名前を明示しない場合、 //< 初期化に使用した変数名がメンバ名となる //< (at2.m == 1 , at2.n == "yy") // ValueTuple は値型のため、nullを格納できない // nullを格納したい場合はnull許容型とする必要がある (int, string) vt6 = null; //< エラー // Tuple はクラスのため、nullで初期化できる Tuple<int, string> t1 = null; //< 正常 // ValueTuple は再割り当て可能(mutable) var vt7 = (1, "vt"); vt6.Item1 = 2; //< 正常 // Tuple 、匿名型は再割り当て不可(immutable) var t2 = Tuple.Create(1, "vt"); var at3 = new { Item1 = 1, Item2 = "vt" }; t2.Item1 = 2; //< エラー at3.Item1 = 2; //< エラー
Tuple 型の構築は ValueTuple 型と異なり、テンプレート型引数を指定するか、Tuple.Create() メソッドを用いる必要がある。
ValueTuple 型は括弧内に型を列記するだけでよく簡便である。
ValueTuple は構造体として実装されているため null を格納できないが、値の更新(再割り当て)が可能である。